﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Xml;
using Higgs.Web.AtomPub.ActionResults;
using Higgs.Web.AtomPub.Configurations;
using Higgs.Web.AtomPub.Models;

namespace Higgs.Web.AtomPub
{
    public static class RenderHelpers
    {
        public static string ToUrn(this Guid id)
        {
            return id.ToString().ToUrn();
        }

        public static string ToUrn(this string id)
        {
            return "urn:uuid:" + id;
        }

        public static string ToAbsoluteUrl(this HttpContextBase context, string logicalPath)
        {
            var absoluteUri = context.Request.Url.AbsoluteUri;
            var absolutePath = context.Request.Url.AbsolutePath;
            var severPath = absoluteUri.Substring(0, absoluteUri.IndexOf(absolutePath));

            return severPath + context.ToClientUrl(logicalPath);
        }

        #region Utils from PathHelpers in ASP.NET MVC library

        public static string ToClientUrl(this HttpContextBase httpContext, string logicalPath) 
        {
            if (String.IsNullOrEmpty(logicalPath)) return logicalPath; 
 
            // can't call VirtualPathUtility.IsAppRelative since it throws on some inputs
            var isAppRelative = logicalPath[0] == '~';
            if (isAppRelative) 
            {
                var absoluteContentPath = VirtualPathUtility.ToAbsolute(logicalPath, httpContext.Request.ApplicationPath); 
                var modifiedAbsoluteContentPath = httpContext.Response.ApplyAppPathModifier(absoluteContentPath);
                return httpContext.ToClientUrl(modifiedAbsoluteContentPath); 
            } 

            var relativeUrlToDestination = MakeRelative(httpContext.Request.Path, logicalPath);
            var absoluteUrlToDestination = MakeAbsolute(httpContext.Request.RawUrl, relativeUrlToDestination);

            return absoluteUrlToDestination; 
        }
 
        private static string MakeAbsolute(string basePath, string relativePath) 
        { 
            string query; 
            basePath = StripQuery(basePath, out query);

            return VirtualPathUtility.Combine(basePath, relativePath);
        }
 
        private static string MakeRelative(string fromPath, string toPath) 
        {
            var relativeUrl = VirtualPathUtility.MakeRelative(fromPath, toPath); 
            if (String.IsNullOrEmpty(relativeUrl) || relativeUrl[0] == '?') relativeUrl = "./" + relativeUrl;
            
            return relativeUrl; 
        }
 
        private static string StripQuery(string path, out string query) 
        { 
            var queryIndex = path.IndexOf('?');
            if (queryIndex >= 0)
            { 
                query = path.Substring(queryIndex);
                return path.Substring(0, queryIndex);
            }
            
            query = null;
            return path; 
        }

        #endregion

        public static ActionResult RenderService()
        {
            var setting = AtomPubConfigSection.Current;
            var writer = new StringWriterWithEncoding(Encoding.UTF8);
            var doc = new XmlTextWriter(writer)
            {
                Formatting = Formatting.Indented
            };

            doc.WriteStartDocument();
            doc.WriteStartElement("service");
            doc.WriteAttributeString("xmlns", "http://www.w3.org/2007/app");
            doc.WriteAttributeString("xmlns", "atom", null, "http://www.w3.org/2005/Atom");

            #region Workspace

            doc.WriteStartElement("workspace");
            doc.WriteElementString("atom", "title", setting.WorkspaceTitle);
            
            #region Feed Section

            doc.WriteStartElement("collection");
            doc.WriteAttributeString("href", setting.Feed.Path);
            doc.WriteElementString("atom", "title", null, setting.Feed.Title);
            doc.WriteElementString("accept", "application/atom+xml;type=entry");

            doc.WriteStartElement("categories");
            doc.WriteAttributeString("fixed", "no");
            doc.WriteEndElement();

            doc.WriteEndElement();

            #endregion
            
            #region Media Section

            doc.WriteStartElement("collection");
            doc.WriteAttributeString("href", setting.Media.Path);
            doc.WriteElementString("atom", "title", null, setting.Media.Title);

            for(var i = 0; i <setting.Media.AcceptTypes.Count; i++)
            {
                doc.WriteElementString("accept", setting.Media.AcceptTypes[i].MimeType);
            }

            doc.WriteStartElement("categories");
            doc.WriteAttributeString("fixed", "yes");
            doc.WriteEndElement();

            doc.WriteEndElement();

            #endregion

            doc.WriteEndElement();

            #endregion

            doc.WriteEndElement();
            doc.WriteEndDocument();
            doc.Flush();
            doc.Close();

            return new AtomResult(writer);
        }
                
        public static ActionResult Render(this IEnumerable<IEntry> entries, HttpContextBase context, DateTime? lastUpdated, string nextPageUrl, bool publicVersion)
        {
            var setting = AtomPubConfigSection.Current.Feed;
            var writer = new StringWriterWithEncoding(Encoding.UTF8);
            var doc = new XmlTextWriter(writer)
            {
                Formatting = Formatting.Indented
            };
            
            doc.WriteStartDocument();
            doc.WriteStartElement("feed");
            doc.WriteAttributeString("xmlns", "http://www.w3.org/2005/Atom");
            doc.WriteElementString("id", setting.Guid.ToUrn());
            doc.WriteElementString("title", setting.Title);
            doc.WriteElementString("generator", AtomPublishingApp.ProductName);

            if(!string.IsNullOrEmpty(setting.Rights)) doc.WriteElementString("rights", setting.Rights);
            if(lastUpdated.HasValue) doc.WriteElementString("updated", XmlConvert.ToString(lastUpdated.Value, XmlDateTimeSerializationMode.Local));

            if (!string.IsNullOrEmpty(setting.HtmlPath))
            {
                doc.WriteStartElement("link");
                doc.WriteAttributeString("rel", "alternate");
                doc.WriteAttributeString("type", "application/xhtml+xml");
                doc.WriteAttributeString("href", context.ToClientUrl(setting.HtmlPath));
                doc.WriteEndElement();
            }

            if (!string.IsNullOrEmpty(nextPageUrl))
            {
                doc.WriteStartElement("link");
                doc.WriteAttributeString("rel", "next");
                doc.WriteAttributeString("type", "application/xhtml+xml");
                doc.WriteAttributeString("href", nextPageUrl);
                doc.WriteEndElement();
            }

            #region Author Infomation
            
            doc.WriteStartElement("author");
            doc.WriteElementString("name", setting.AuthorName);
            if (!string.IsNullOrEmpty(setting.AuthorUrl)) doc.WriteElementString("uri", setting.AuthorUrl);
            doc.WriteEndElement();

            #endregion

            foreach (var entry in entries)
            {
                entry.Render(context, false, doc);
            }

            doc.WriteEndElement();
            doc.WriteEndDocument();
            doc.Flush();
            doc.Close();

            return new AtomResult(writer);
        }

        public static AtomResult Render(this IEntry entry,  HttpContextBase context, bool isStandAlone)
        {
            var writer = new StringWriterWithEncoding(Encoding.UTF8);
            var doc = new XmlTextWriter(writer)
            {
                Formatting = Formatting.Indented
            };

            doc.WriteStartDocument();

            entry.Render(context, isStandAlone, doc);

            doc.WriteEndDocument();
            doc.Flush();
            doc.Close();

            return new AtomResult(writer, AtomType.Entry);
        }

        public static void Render(this IEntry entry, HttpContextBase context, bool isStandAlone, XmlTextWriter doc)
        {
            var setting = AtomPubConfigSection.Current;
            if (!entry.UpdatedDate.HasValue)
            {
                entry.UpdatedDate = entry.EditedDate.HasValue ? entry.EditedDate : entry.PublishedDate;
            }
            
            doc.WriteStartElement("entry");
            if(isStandAlone) doc.WriteAttributeString("xmlns", "http://www.w3.org/2005/Atom");

            doc.WriteElementString("id", entry.Id.ToUrn());
            doc.WriteElementString("title", entry.Title);

            if(entry.PublishedDate.HasValue) doc.WriteElementString("published", XmlConvert.ToString(entry.PublishedDate.Value, XmlDateTimeSerializationMode.Local));
            if(entry.EditedDate.HasValue) doc.WriteElementString("edited", XmlConvert.ToString(entry.EditedDate.Value, XmlDateTimeSerializationMode.Local));
            if(entry.UpdatedDate.HasValue) doc.WriteElementString("updated", XmlConvert.ToString(entry.UpdatedDate.Value, XmlDateTimeSerializationMode.Local));

            #region Content

            doc.WriteStartElement("content");
            doc.WriteAttributeString("type", "html");
            doc.WriteString(entry.ContentHtml);
            doc.WriteEndElement();

            #endregion

            if (!string.IsNullOrEmpty(entry.Url))
            {
                doc.WriteStartElement("link");
                doc.WriteAttributeString("rel", "alternate");
                doc.WriteAttributeString("href", entry.Url);
                doc.WriteAttributeString("type", "text/html");
                doc.WriteEndElement();
            }

            doc.WriteStartElement("link");
            doc.WriteAttributeString("rel", "edit");
            doc.WriteAttributeString("href", context.ToAbsoluteUrl("~/" + setting.Feed.Path + entry.Id));
            doc.WriteAttributeString("type", "text/html");
            doc.WriteEndElement();

            doc.WriteStartElement("author");
            doc.WriteElementString("name", setting.Feed.AuthorName);
            if(!string.IsNullOrEmpty(setting.Feed.AuthorUrl)) doc.WriteElementString("uri", setting.Feed.AuthorUrl);
            doc.WriteEndElement();

            if(!string.IsNullOrEmpty(setting.Feed.Rights)) doc.WriteElementString("rights", setting.Feed.Rights);

            /*
            var categories = entry.
            if(categories != null)
            {
                var catList = categories.ToList();
                foreach(var c in catList)
                {
                    doc.WriteStartElement("category");
                    doc.WriteAttributeString("term", c.Name);
                    doc.WriteEndElement();
                }
            }
            */

            if(entry.Draft)
            {
                doc.WriteStartElement("app", "control", null);
                doc.WriteAttributeString("xmlns", "app", null, "http://www.w3.org/2007/app");
                doc.WriteElementString("app", "draft", null, "yes");
                doc.WriteEndElement();
            }

            doc.WriteEndElement();
        }

        public static AtomResult Render(this IMediaItem media, HttpContextBase context)
        {
            var setting = AtomPubConfigSection.Current;
            var writer = new StringWriterWithEncoding(Encoding.UTF8);
            var doc = new XmlTextWriter(writer)
            {
                Formatting = Formatting.Indented
            };

            doc.WriteStartDocument();

            doc.WriteStartElement("entry");
            doc.WriteAttributeString("xmlns", "http://www.w3.org/2005/Atom");
            doc.WriteElementString("id", media.Id.ToUrn());
            doc.WriteElementString("title", "None");
            doc.WriteElementString("updated", XmlConvert.ToString(media.UpdatedDate, XmlDateTimeSerializationMode.Local));

            doc.WriteStartElement("author");
            doc.WriteElementString("name", "None");
            doc.WriteEndElement();

            doc.WriteStartElement("summary");
            doc.WriteAttributeString("type", "text");
            doc.WriteEndElement();

            doc.WriteStartElement("content");
            doc.WriteAttributeString("type", media.MimeType);
            doc.WriteAttributeString("src", context.ToAbsoluteUrl(media.LogicalPath));
            doc.WriteEndElement();
            
            doc.WriteStartElement("link");
            doc.WriteAttributeString("rel", "edit-media");
            doc.WriteAttributeString("href", context.ToAbsoluteUrl("~/" + setting.Media.Path + "/" + media.Id));
            doc.WriteEndElement();
            
            doc.WriteStartElement("link");
            doc.WriteAttributeString("rel", "edit");
            doc.WriteAttributeString("href", context.ToAbsoluteUrl("~/" + setting.Media.Path + "/" + media.Id));
            doc.WriteEndElement();

            doc.WriteEndElement();
            doc.WriteEndDocument();
            doc.Flush();
            doc.Close();

            return new AtomResult(writer);
        }
    }
}
